package com.yourenbang.lingyun.transaction.web.service.impl.pay;

import com.alibaba.fastjson.JSON;
import com.alipay.api.AlipayClient;
import com.alipay.api.CertAlipayRequest;
import com.alipay.api.DefaultAlipayClient;
import com.github.binarywang.wxpay.config.WxPayConfig;
import com.github.binarywang.wxpay.service.WxPayService;
import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
import com.yourenbang.lingyun.constants.BizCodeMessages;
import com.yourenbang.lingyun.constants.enums.PlatNoEnums;
import com.yourenbang.lingyun.constants.enums.fund.AccountType;
import com.yourenbang.lingyun.constants.enums.pay.PayFromEnum;
import com.yourenbang.lingyun.constants.enums.pay.PayTypeEnum;
import com.yourenbang.lingyun.constants.enums.table.AllyChannelEnum;
import com.yourenbang.lingyun.core.config.datasource.DataSources;
import com.yourenbang.lingyun.core.config.datasource.RoutingDataSource;
import com.yourenbang.lingyun.core.exception.BizRuntimeException;
import com.yourenbang.lingyun.core.exception.UserBizException;
import com.yourenbang.lingyun.core.message.Result;
import com.yourenbang.lingyun.core.model.vo.PageVO;
import com.yourenbang.lingyun.core.support.PageFrom;
import com.yourenbang.lingyun.core.util.BizAssertUtils;
import com.yourenbang.lingyun.core.util.KLog;
import com.yourenbang.lingyun.domain.*;
import com.yourenbang.lingyun.mapper.*;
import com.yourenbang.lingyun.transaction.mapper.TransactionPayTypeMapper;
import com.yourenbang.lingyun.transaction.web.domain.dto.pay.*;
import com.yourenbang.lingyun.transaction.web.domain.po.TSPayType;
import com.yourenbang.lingyun.transaction.web.domain.po.TsPayChannel;
import com.yourenbang.lingyun.transaction.web.service.pay.PayTypeService;
import com.yourenbang.transaction.api.service.pay.PayTypeApiService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;

@Slf4j
@Service("payTypeService")
public class PayTypeServiceImpl implements PayTypeService {

    @Autowired
    private PayTypeMapper payTypeMapper;
    @Autowired
    private TransactionPayTypeMapper transactionPayTypeMapper;
    @Autowired
    private PayChannelMapper payChannelMapper;
    @Autowired
    private PayChannelParamMapper payChannelParamMapper;
    @Autowired
    private PayTypeApiService payTypeApiService;
    @Autowired
    private UserBaseMapper userBaseMapper;
    @Autowired
    private AllyChannelMapper allyChannelMapper;

    /**
     * app获取支付方式
     */
    @Override
    public List<TSPayType> payType(PayTypeDTO dto) {


        UserBase userBase = userBaseMapper.selectByPrimaryKey(dto.getUserBaseId());
        if (Objects.isNull(userBase)) {
            throw new UserBizException(BizCodeMessages.USER_UNEXIST);
        }

        PayTypeExample payTypeExample = new PayTypeExample();
        PayTypeExample.Criteria payTypeCriteria = payTypeExample.createCriteria();
        payTypeCriteria.andPayFromEqualTo(dto.getPayFrom())
                .andPlatNoEqualTo(userBase.getPlatNo())
                .andStatusEqualTo(TSPayType.STATUS_ON);
        List<PayType> payTypes = payTypeMapper.selectByExample(payTypeExample);

        if (payTypes.isEmpty()) {
            KLog.error(BizCodeMessages.WITHDRAW_PAY_TYPE_OFF, "支付通道不可用");
        }

        List<TSPayType> tsPayTypes = new ArrayList<>();
        for (PayType type : payTypes) {
            if (StringUtils.equals(type.getCode(), PayTypeEnum.BALANCE.getCode()) && userBase.getUserType() >= 1) {
                continue;
            }
            TSPayType tsType = new TSPayType();
            BeanUtils.copyProperties(type, tsType);
            //微信
            if (StringUtils.equals(tsType.getCode(), PayTypeEnum.WECHAT.getCode())) {

                Map<String, String> channelParams = payTypeApiService.getPayParams(PayTypeEnum.getPayType(tsType.getCode()), PayFromEnum.getPayFrom(tsType.getPayFrom()));
                Map<String, Object> params = tsType.getParams();
                params.put("appid", channelParams.get("appid"));

            } else if (PayTypeEnum.LINGHUISUAN.getCode().equals(type.getCode())) {

                // 灵会算,检查签约,未签约,提示签约
//                Map<String, Object> params = tsType.getParams();
//                Result<String> result = lingHuiSuanService.alreadySign(PayFromEnum.getPayFrom(tsType.getPayFrom()),
//                        dto.getUserBaseId());
//
//                if (result.isSuccess() && ObjectUtils.isNotEmpty(result.getData())) {
//                    String signUrl = result.getData();
//                    params.put("fullSignUrl", signUrl);
//                    params.put("isSign", ObjectUtils.isEmpty(signUrl));
//                }

            }
            tsPayTypes.add(tsType);
        }

        return tsPayTypes;
    }


    /**
     * 支付方式-列表
     */
    @RoutingDataSource(DataSources.SLAVE_DB)
    @Override
    public PageVO<TSPayType> list(PayTypeListDTO dto) {
        PageFrom.startPage(dto.getPageNum(), dto.getPageSize());

        List<TSPayType> payType = transactionPayTypeMapper.payTypeList(dto);
        for (TSPayType type : payType) {
            PayTypeEnum payTypeEnum = PayTypeEnum.getPayType(type.getCode());
            if (payTypeEnum != null) {
                type.setPayTypeName(payTypeEnum.getName());
            }
            PayFromEnum payFromEnum = PayFromEnum.getPayFrom(type.getPayFrom());
            if (payFromEnum != null) {
                type.setPayFromName(payFromEnum.getName());
            }
            if (type.getPayChannel() != null) {
                PayChannel payChannel = payChannelMapper.selectByPrimaryKey(type.getPayChannel());
                if (payChannel != null) {
                    type.setPayChannelName(payChannel.getName());
                }
            }
        }
        return new PageVO<>(payType);
    }

    /**
     * 获取支付宝客户端
     *
     * @param payFrom 支付场景
     */
    @Override
    public AlipayClient getAlipayClient(PayFromEnum payFrom) {
        Map<String, String> paramMap = payTypeApiService.getPayParams(PayTypeEnum.ALIPAY, payFrom);
        return getAlipayClientByParamMap(paramMap);

    }

    /**
     * 获取支付宝客户端
     *
     * @param payChannelId 支付通道
     */
    @Override
    public AlipayClient getAlipayClientByChannel(Integer payChannelId) {
        Map<String, String> paramMap = payTypeApiService.getPayParamsByChannel(payChannelId);
        return getAlipayClientByParamMap(paramMap);
    }

    /**
     * 获取支付宝客户端
     *
     * @param paramMap 支付参数
     */
    @Override
    public AlipayClient getAlipayClientByParamMap(Map<String, String> paramMap) {
        log.debug("【获取支付宝客户端paramMap】入参:{}", JSON.toJSONString(paramMap));

        //是否 证书加签
        if (StringUtils.isNotBlank(paramMap.get("appCertPath"))) {
            return getAlipayCertClient(paramMap);
        }

        AlipayClient client = new DefaultAlipayClient(
                AlipayProperties.gatewayUrl,
                paramMap.get("appid"),
                paramMap.get("prikey"),
                "json",
                "utf-8",
                paramMap.get("pubkey"),
                "RSA2");

        return client;
    }

    /**
     * 获取微信客户端
     *
     * @param payFrom 支付场景
     */
    @Override
    public WxPayService getWxPayService(PayFromEnum payFrom) {
        Map<String, String> paramMap = payTypeApiService.getPayParams(PayTypeEnum.WECHAT, payFrom);

        WxPayConfig payConfig = new WxPayConfig();
        payConfig.setAppId(paramMap.get("appid"));
        payConfig.setMchId(paramMap.get("mchid"));
        payConfig.setMchKey(paramMap.get("mchkey"));
        payConfig.setSignType("MD5");

        // 可以指定是否使用沙箱环境
        payConfig.setUseSandboxEnv(false);

        log.debug("【获取微信客户端】{},入参:{}", JSON.toJSONString(payConfig), payFrom);
        WxPayService wxPayService = new WxPayServiceImpl();
        wxPayService.setConfig(payConfig);
        return wxPayService;
    }

    /**
     * 获取微信客户端
     *
     * @param payChannelId 支付通道
     */
    @Override
    public WxPayService getWxPayServiceByChannel(Integer payChannelId) {
        Map<String, String> paramMap = payTypeApiService.getPayParamsByChannel(payChannelId);

        WxPayConfig payConfig = new WxPayConfig();
        payConfig.setAppId(paramMap.get("appid"));
        payConfig.setMchId(paramMap.get("mchid"));
        payConfig.setMchKey(paramMap.get("mchkey"));
        payConfig.setSignType("MD5");

        // 可以指定是否使用沙箱环境
        payConfig.setUseSandboxEnv(false);

        log.debug("【获取微信客户端】{},入参:{}", JSON.toJSONString(payConfig), payChannelId);
        WxPayService wxPayService = new WxPayServiceImpl();
        wxPayService.setConfig(payConfig);
        return wxPayService;
    }

    /**
     * 支付方式-保存
     */
    @Override
    public void add(PayTypeAddDTO dto) {
        PayType oldType = transactionPayTypeMapper.getPayType(dto.getCode(), dto.getPayFrom());
        if (oldType != null) {
            KLog.warn("【支付方式-保存】已存在该支付方式");
            return;
        }
        PayType payType = new PayType();
        payType.setCode(dto.getCode());
        payType.setPayFrom(dto.getPayFrom());
        payType.setName(PayTypeEnum.getPayType(dto.getCode()).getName());
        payType.setStatus(new Short(dto.getStatus()));
        payType.setPlatNo(dto.getPlatNo());
        payType.setPayChannel(dto.getPayChannelId());
        int in = payTypeMapper.insertSelective(payType);
        BizAssertUtils.assertOne(in, "添加支付方式失败");
    }

    /**
     * 支付方式-修改
     */
    @Override
    public void update(PayTypeAddDTO dto) {
        PayType payType = payTypeMapper.selectByPrimaryKey(dto.getPayTypeId());
        if (payType == null) {
            throw new BizRuntimeException(BizCodeMessages.UNKNOWN_ERROR, "未找到支付方式");
        }
        PayType upType = new PayType();
        upType.setPayTypeId(payType.getPayTypeId());
        upType.setName(PayTypeEnum.getPayType(dto.getCode()).getName());
        upType.setStatus(new Short(dto.getStatus()));
        upType.setPayChannel(dto.getPayChannelId());
        int upc = payTypeMapper.updateByPrimaryKeySelective(upType);
        BizAssertUtils.assertOne(upc, "修改支付方式失败");
    }

    /**
     * 支付方式-删除
     */
    @Override
    public void delete(PayTypeDeleteDTO dto) {
        payTypeMapper.deleteByPrimaryKey(dto.getPayTypeId());
    }

    /**
     * 支付方式-下拉支付通道
     */
    @RoutingDataSource(DataSources.SLAVE_DB)
    @Override
    public List<PayChannel> getChannel(GetChannelDTO dto) {
        PayChannelExample example = new PayChannelExample();
        example.createCriteria().andPlatNoEqualTo(dto.getPlatNo());
        return payChannelMapper.selectByExample(example);
    }

    /**
     * 支付通道-列表
     */
    @RoutingDataSource(DataSources.SLAVE_DB)
    @Override
    public PageVO<TsPayChannel> channelList(PayChannelListDTO dto) {
        PageFrom.startPage(dto.getPageNum(), dto.getPageSize());
        List<TsPayChannel> payChannels = transactionPayTypeMapper.payChannelList(dto);
        for (TsPayChannel channel : payChannels) {
            PayChannelParamExample payChannelParamExample = new PayChannelParamExample();
            PayChannelParamExample.Criteria payChannelParamExampleCriteria = payChannelParamExample.createCriteria();
            payChannelParamExampleCriteria.andPayChannelIdEqualTo(channel.getPayChannelId());
            payChannelParamExample.setOrderByClause("code asc");
            List<PayChannelParam> payChannelParams = payChannelParamMapper.selectByExample(payChannelParamExample);
            channel.setPayChannelParams(payChannelParams);
        }
        return new PageVO<>(payChannels);
    }

    /**
     * 支付通道-保存
     */
    @Override
    public void channelAdd(PayChannelAddDTO dto) {
        PayChannel channel = new PayChannel();
        channel.setName(dto.getName());
        channel.setPlatNo(dto.getPlatNo());
        int inChannel = payChannelMapper.insertSelective(channel);
        BizAssertUtils.assertOne(inChannel, "添加支付通道失败");
        for (PayChannelParam param : dto.getChannelParams()) {
            param.setPayChannelId(channel.getPayChannelId());
            int inParam = payChannelParamMapper.insertSelective(param);
            BizAssertUtils.assertOne(inParam, "添加支付通道参数失败");
        }
    }

    /**
     * 支付通道-修改
     */
    @Transactional
    @Override
    public void channelUpdate(PayChannelAddDTO dto) {
        PayChannel channel = payChannelMapper.selectByPrimaryKey(dto.getPayChannelId());
        if (channel == null) {
            throw new BizRuntimeException(BizCodeMessages.UNKNOWN_ERROR, "未找到支付通道");
        }
        if (StringUtils.isNotBlank(dto.getName())) {
            PayChannel upChannel = new PayChannel();
            upChannel.setPayChannelId(channel.getPayChannelId());
            upChannel.setName(dto.getName());
            int uocc = payChannelMapper.updateByPrimaryKeySelective(upChannel);
            BizAssertUtils.assertOne(uocc, "修改支付通道失败");
        }
        if (!CollectionUtils.isEmpty(dto.getChannelParams())) {
            //删除原参数
            PayChannelParamExample payChannelParamExample = new PayChannelParamExample();
            PayChannelParamExample.Criteria payChannelParamExampleCriteria = payChannelParamExample.createCriteria();
            payChannelParamExampleCriteria.andPayChannelIdEqualTo(channel.getPayChannelId());
            payChannelParamMapper.deleteByExample(payChannelParamExample);

            for (PayChannelParam param : dto.getChannelParams()) {
                param.setPayChannelId(channel.getPayChannelId());
                int inParam = payChannelParamMapper.insertSelective(param);
                BizAssertUtils.assertOne(inParam, "添加支付通道参数失败");
            }
        }
    }

    /**
     * 支付通道-删除
     */
    @Transactional
    @Override
    public void channelDelete(PayChannelDeleteDTO dto) {
        PayChannelExample payChannelExample = new PayChannelExample();
        PayChannelExample.Criteria payChannelExampleCriteria = payChannelExample.createCriteria();
        payChannelExampleCriteria.andPayChannelIdEqualTo(dto.getPayChannelId());
        int dec = payChannelMapper.deleteByExample(payChannelExample);
        BizAssertUtils.assertOne(dec, "删除支付通道失败");

        //删除参数
        PayChannelParamExample payChannelParamExample = new PayChannelParamExample();
        PayChannelParamExample.Criteria payChannelParamExampleCriteria = payChannelParamExample.createCriteria();
        payChannelParamExampleCriteria.andPayChannelIdEqualTo(dto.getPayChannelId());
        payChannelParamMapper.deleteByExample(payChannelParamExample);
    }

    /**
     * 获取支付宝客户端(证书)
     */
    private AlipayClient getAlipayCertClient(Map<String, String> paramMap) {
        try {
            String basePath = this.queryAlipayCertPath(paramMap);

            CertAlipayRequest certAlipayRequest = new CertAlipayRequest();
            certAlipayRequest.setServerUrl(AlipayProperties.gatewayUrl);
            certAlipayRequest.setAppId(paramMap.get("appid"));
            certAlipayRequest.setPrivateKey(paramMap.get("prikey"));
            certAlipayRequest.setFormat("json");
            certAlipayRequest.setCharset("utf-8");
            certAlipayRequest.setSignType("RSA2");
            certAlipayRequest.setCertPath(basePath + paramMap.get("appCertPath"));
            certAlipayRequest.setAlipayPublicCertPath(basePath + paramMap.get("alipayCertPath"));
            certAlipayRequest.setRootCertPath(basePath + paramMap.get("alipayRootCertPath"));

            return new DefaultAlipayClient(certAlipayRequest);
        } catch (Exception e) {
            KLog.error("【 获取提现支付宝客户端】异常:{}", e.getMessage(), e);
            throw new BizRuntimeException(BizCodeMessages.UNKNOWN_ERROR, "获取提现支付宝客户端失败");
        }
    }

    public String queryAlipayCertPath(Map<String, String> paramMap) {
//        String basePath = ResourceUtils.getURL("classpath:").getPath();
        String basePath = paramMap.get("basecertpath");
        basePath = this.initCart(basePath, paramMap);

        return basePath;
    }

    private String initCart(String basePath, Map<String, String> paramMap) {
//        if (basePath.contains("jar!")) {
//            if (basePath.startsWith("file:")) {
//                basePath = basePath.replace("file:", "");
//            }
        String appCertPath = paramMap.get("appCertPath");
        this.checkAndcopyCart(basePath, appCertPath);
        String alipayCertPath = paramMap.get("alipayCertPath");
        this.checkAndcopyCart(basePath, alipayCertPath);
        String alipayRootCertPath = paramMap.get("alipayRootCertPath");
        this.checkAndcopyCart(basePath, alipayRootCertPath);
//        }
        return basePath;
    }

    /**
     * 查找在该文件路径下是否已经存在这个文件，如果不存在，则拷贝文件
     */
    private void checkAndcopyCart(String basePath, String path) {
        InputStream cartInputStream = null;
        OutputStream cartOutputStream = null;
        String cartPath = basePath + path;
        File cartFile = new File(cartPath);
        File parentFile = cartFile.getParentFile();
        parentFile.mkdirs();
        try {
            if (!cartFile.exists()) {
                cartInputStream = PayTypeServiceImpl.class.getResourceAsStream(path);
                cartOutputStream = new FileOutputStream(cartFile);
                byte[] buf = new byte[1024];
                int bytesRead;
                while ((bytesRead = cartInputStream.read(buf)) != -1) {
                    cartOutputStream.write(buf, 0, bytesRead);
                }
                cartInputStream.close();
                cartOutputStream.close();
            }
        } catch (IOException e) {
            throw new BizRuntimeException(BizCodeMessages.UNKNOWN_ERROR, e.getMessage());
        }
    }

    /**
     * 检查盟友是否为庄家身份
     *
     * @param userBaseId id
     * @return true false
     */
    private boolean checkBankerRole(String userBaseId) {

        AllyChannelExample example = new AllyChannelExample();
        example.createCriteria().andUserBaseIdEqualTo(userBaseId)
                .andAllyRoleEqualTo(AllyChannelEnum.AllyRoleEnum.ALLY_ROLE_50.getCode());
        long queryCount = allyChannelMapper.countByExample(example);
        if (queryCount > 0) {
            return Boolean.TRUE;
        }
        return Boolean.FALSE;
    }
}
